package com.wfl.sampling.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSON;
import com.google.gson.Gson;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wfl.sampling.config.WxPayConfig;
import com.wfl.sampling.handler.WxChatResponseInfo;
import com.wfl.sampling.service.WxchatSignService;
import com.wfl.sampling.utils.WechatPayValidatorForRequestUtils;
import lombok.extern.slf4j.Slf4j;
import okhttp3.HttpUrl;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

@Service
@Slf4j
public class WxchatSignServiceImpl implements WxchatSignService {
    @Autowired
    private WxPayConfig wxPayConfig;

    // Authorization: <schema> <token>
    // GET - getToken("GET", httpurl, "")
    // POST - getToken("POST", httpurl, json)
    //获取请求签名
    @Override
    public String getSign(String method, HttpUrl url, String body) throws NoSuchAlgorithmException, SignatureException, InvalidKeySpecException, InvalidKeyException, UnsupportedEncodingException {
        try {
            return "WECHATPAY2-SHA256-RSA2048 " + getToken(method, url, body);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    @Override
    public String getToken(String method, HttpUrl url, String body) throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        String nonceStr = WxPayConfig.getNonceStr();
        long timestamp = System.currentTimeMillis() / 1000;
        String message = buildMessage(method, url, timestamp, nonceStr, body);
        String signature = sign(message.getBytes("utf-8"));
        return "mchid=\"" + wxPayConfig.getMchId() + "\","
                + "nonce_str=\"" + nonceStr + "\","
                + "timestamp=\"" + timestamp + "\","
                + "serial_no=\"" + wxPayConfig.getWxCertno() + "\","
                + "signature=\"" + signature + "\"";
    }

    @Override
    public String sign(byte[] message) throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(WxPayConfig.getPrivateKey());
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }
    @Override
    public String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
        String canonicalUrl = url.encodedPath();
        if (url.encodedQuery() != null) {
            canonicalUrl += "?" + url.encodedQuery();
        }
        return method + "\n"
                + canonicalUrl + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + body + "\n";
    }
     @Override
     public String bulidMessagePay(String appid, String nonceStr, String Package, String timeStamp){
        return appid + "\n"
                + timeStamp + "\n"
                + nonceStr + "\n"
                + Package + "\n";
     }

    //生成微信（下单，退款）请求
    @Override
    public WxChatResponseInfo WxChatdoPay(String mothed, String url, Map<String, Object> param) throws UnsupportedEncodingException, NoSuchAlgorithmException, SignatureException, InvalidKeySpecException, InvalidKeyException {
        WxChatResponseInfo wxChatResponseInfo = new WxChatResponseInfo();
        // 创建Httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        String resultString = "";
        // 创建Http Post请求
        HttpPost httpPost = new HttpPost(url);
        httpPost.setHeader("Authorization", getSign(mothed, HttpUrl.parse(url), com.alibaba.fastjson.JSON.toJSONString(param)));
        httpPost.setHeader("Accept", "application/json");
        httpPost.setHeader("Content-Type", "application/json");
        // 将参数map转换为json字符串的格式添加到请求体中
        if (param != null) {
            String jsonParam = JSON.toJSONString(param);
            StringEntity entity = new StringEntity(jsonParam, "utf-8");
            httpPost.setEntity(entity);
        }
        // 执行http请求
        try {
            response = httpClient.execute(httpPost);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        try {
            resultString = EntityUtils.toString(response.getEntity(), "utf-8");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        wxChatResponseInfo.setCode(response.getStatusLine().getStatusCode());
        wxChatResponseInfo.setResponseBody(resultString);
        return wxChatResponseInfo;
    }

    //生成微信查询（订单，退款）请求
    @Override
    public WxChatResponseInfo WxChatQuery(String queryType, String out_trade_no) throws  UnsupportedEncodingException, NoSuchAlgorithmException, SignatureException, InvalidKeySpecException, InvalidKeyException{
        // 创建Httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        String url = "";
        if(queryType.equals("order")) {
            // 创建Http Get请求
            url = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + out_trade_no +"?mchid=" +wxPayConfig.getMchId();
        }else {
            url = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/" + out_trade_no;
        }
        HttpGet httpGet = new HttpGet(url);
        httpGet.setHeader("Authorization", getSign("GET", HttpUrl.parse(url), ""));
        httpGet.setHeader("Accept", "application/json");
        // 执行http请求
        try {
            response = httpClient.execute(httpGet);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        WxChatResponseInfo wxChatResponseInfo = new WxChatResponseInfo();
        wxChatResponseInfo.setCode(response.getStatusLine().getStatusCode());
        try {
            wxChatResponseInfo.setResponseBody(EntityUtils.toString(response.getEntity(), "utf-8"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return wxChatResponseInfo;
    }

    //关闭订单请求
    @Override
    public Map<String, Integer> WxChatClose(String out_trade_no) throws  UnsupportedEncodingException, NoSuchAlgorithmException, SignatureException, InvalidKeySpecException, InvalidKeyException {
        Map<String,Object> body = new HashMap<>();
        body.put("mchid",wxPayConfig.getMchId());
        // 创建Httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        // 创建Http Post请求
        String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + out_trade_no + "/close";
        HttpPost httpPost = new HttpPost(url);
        httpPost.setHeader("Authorization", getSign("POST", HttpUrl.parse(url), com.alibaba.fastjson.JSON.toJSONString(body)));
        httpPost.setHeader("Accept", "application/json");
        httpPost.setHeader("Content-Type", "application/json");
        // 执行http请求
        try {
            response = httpClient.execute(httpPost);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        Map<String,Integer> map = new HashMap<>();
        map.put("code",response.getStatusLine().getStatusCode());
        return map;
    }
    //获取验证签名器
    @Override
    public Verifier getVerifier() throws IOException {
        // 获取商户私钥
        final PrivateKey privateKey = WxPayConfig.getPrivateKey();

        // 私钥签名对象
        PrivateKeySigner privateKeySigner = new PrivateKeySigner(wxPayConfig.getWxCertno(), privateKey);

        // 身份认证对象
        WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(wxPayConfig.getMchId(), privateKeySigner);

        // 获取证书管理器实例
        CertificatesManager certificatesManager = CertificatesManager.getInstance();

        try {
            // 向证书管理器增加需要自动更新平台证书的商户信息
            certificatesManager.putMerchant(wxPayConfig.getMchId(), wechatPay2Credentials, wxPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));
        } catch (IOException | GeneralSecurityException | HttpCodeException e) {
            e.printStackTrace();
        }
        try {
            return certificatesManager.getVerifier(wxPayConfig.getMchId());
        } catch (NotFoundException e) {
            throw new RuntimeException(e);
        }
    }
    //签证签名
    @Override
    public boolean wxPaySuccessCallback(HttpServletRequest request, HttpServletResponse response, Verifier verifier) {
        Gson gson = new Gson();
        // 1.处理通知参数
        final String body = WxPayConfig.readData(request);
        HashMap bodyMap = gson.fromJson(body, HashMap.class);

        // 2.签名验证
        WechatPayValidatorForRequestUtils wechatForRequest = new WechatPayValidatorForRequestUtils(verifier, body, (String) bodyMap.get("id"));
        try {
            return wechatForRequest.validate(request);
        } catch (IOException e) {
            log.info("验证器验签过程报错");
        }
        return false;
    }

    //获取解密的回调报文
    @Override
     public String Outbody(Map<String, Object> bodyMap) {
        // 通知数据
        Map<String, Object> resourceMap = BeanUtil.beanToMap(bodyMap.get("resource"));
        // 数据密文
        String ciphertext = (String) resourceMap.get("ciphertext");
        // 随机串
        String nonce = (String) resourceMap.get("nonce");
        // 附加数据
        String associateData = (String) resourceMap.get("associated_data");
        AesUtil aesUtil = new AesUtil(wxPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));
        try {
            return aesUtil.decryptToString(associateData.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
        } catch (GeneralSecurityException e) {
            log.info("解密错误");
            throw new RuntimeException(e);
        }
    }
}
